/**
 * Created by clakeboy on 15/11/2.
 */
//方法别名
var v = cp.v;
//画布画笔
//空间组
var GRABABLE_MASK_BIT = 1<<31;
//非空间组
var NOT_GRABABLE_MASK = ~GRABABLE_MASK_BIT;
var FENCE_MASK = 1;
var BALL_MASK = 2;
//物理游戏主类
var phy = function(){
    //画布画笔
    this.canvas = null;
    this.ctx = null;
    //主物理主空间
    var space = this.space = new cp.Space();
    this.remainder = 0;
    this.fps = 0;
    this.mouse = v(0,0);
    this.simulationTime = 0;
    this.drawTime = 0;
    this.scale = 1;
    this.moveShape=null;
    var self = this;
    //将画布坐标转换为笛卡系坐标
    var canvas2point = this.canvas2point = function(x, y) {
        return v(x / self.scale, self.height - y / self.scale);
    };
    //将笛卡系坐标转化为画布坐标
    this.point2canvas = function(point) {
        return v(point.x * self.scale, (self.height - point.y) * self.scale);
    };



    //鼠标或TOUCH事件
    function beginMove(e) {
        e.preventDefault();
        var ox,oy;
        if (e.touches) {
            ox = e.touches[0].pageX;
            oy = e.touches[0].pageY;
        } else {
            ox = e.offsetX;
            oy = e.offsetY;
        }
        self.mouse = canvas2point(ox, oy);

        if(!self.mouseJoint) {
            var point = canvas2point(ox, oy);

            var shape = space.pointQueryFirst(point, BALL_MASK, cp.NO_GROUP);
            if(shape){
                shape.move_flag = true;
                var body = shape.body;
                var mouseJoint = self.mouseJoint = new cp.PivotJoint(mouseBody, body, v(0,0), body.world2Local(point));
                mouseJoint.maxForce = 50000;
                mouseJoint.errorBias = Math.pow(1 - 0.15, 60);
                space.addConstraint(mouseJoint);
                self.moveShape = shape;
            }
        }
    }

    function ballMove(e) {
        var ox,oy;
        if (e.touches) {
            ox = e.touches[0].pageX;
            oy = e.touches[0].pageY;
        } else {
            ox = e.offsetX;
            oy = e.offsetY;
        }
        self.mouse = canvas2point(ox, oy);
    }

    function endMove(e) {
        if(self.mouseJoint) {
            self.moveShape.move_flag = false;
            self.moveShape = null;
            space.removeConstraint(self.mouseJoint);
            self.mouseJoint = null;
        }
    }

    //跟随鼠标的静态刚体
    var mouseBody = this.mouseBody = new cp.Body(Infinity, Infinity);
    //画布事件绑定
    this.canvasEvent = function() {
        //禁止右键功能
        this.canvas.oncontextmenu = function(e) { return false; }
        //画布鼠标点击时,出现连接带刚体
        this.canvas.addEventListener('touchstart',beginMove,false);
        this.canvas.addEventListener('mousedown',beginMove,false);
        this.canvas.addEventListener('touchmove',ballMove,false);
        this.canvas.addEventListener('mousemove',ballMove,false);
        window.addEventListener('touchend',endMove,false);
        window.addEventListener('mouseup',endMove,false);
    };

    this.init = function(canvas_name,width,height) {
        this.canvas = document.getElementById(canvas_name);
        this.canvas.width = width-2;
        this.canvas.height = height-2;
        this.ctx = this.canvas.getContext('2d');
        this.width = width;
        this.height = height;
        this.canvasEvent();
    };

    //更新空间状态
    this.update = function(dt) {
        this.space.step(dt);
    };

    //画出空间的图形
    this.draw = function() {
        var ctx = this.ctx;

        var self = this;

        // Draw shapes
        ctx.strokeStyle = 'black';
        ctx.clearRect(0, 0, this.width, this.height);

        this.ctx.font = "16px sans-serif";
        this.ctx.lineCap = 'round';

        this.space.eachShape(function(shape) {
            ctx.fillStyle = shape.style();
            shape.draw(ctx, self.scale, self.point2canvas);
        });

        // Draw collisions
        /*
         ctx.strokeStyle = "red";
         ctx.lineWidth = 2;

         var arbiters = this.space.arbiters;
         for (var i = 0; i < arbiters.length; i++) {
         var contacts = arbiters[i].contacts;
         for (var j = 0; j < contacts.length; j++) {
         var p = this.point2canvas(contacts[j].p);

         ctx.beginPath()
         ctx.moveTo(p.x - 2, p.y - 2);
         ctx.lineTo(p.x + 2, p.y + 2);
         ctx.stroke();

         ctx.beginPath();
         ctx.moveTo(p.x + 2, p.y - 2);
         ctx.lineTo(p.x - 2, p.y + 2);
         ctx.stroke();
         }
         }*/

        if (this.mouseJoint) {
            //ctx.beginPath();
            //var c = this.point2canvas(this.mouseBody.p);
            //ctx.arc(c.x, c.y, this.scale * 5, 0, 2*Math.PI, false);
            //ctx.fill();
            //ctx.stroke();
        }

        this.space.eachConstraint(function(c) {
            if(c.draw) {
                c.draw(ctx, self.scale, self.point2canvas);
            }
        });

        //this.drawInfo();
    };

    //开始运行
    this.run = function() {
        this.running = true;

        var self = this;

        var lastTime = 0;
        var step = function(time) {
            self.step(time - lastTime);
            lastTime = time;

            if (self.running) {
                raf(step);
            }
        };

        step(0);
    };

    //每步执行
    this.step = function(dt) {
        space.addCollisionHandler(BALL_MASK,FENCE_MASK,function(abtr){
            if (abtr.a['move_flag']) {
                return false;
            } else {
                return true;
            }
        });
        // Update FPS
        if(dt > 0) {
            this.fps = 0.9*this.fps + 0.1*(1000/dt);
        }

        // Move mouse body toward the mouse
        var newPoint = v.lerp(this.mouseBody.p, this.mouse, 0.25);
        this.mouseBody.v = v.mult(v.sub(newPoint, this.mouseBody.p), 60);
        this.mouseBody.p = newPoint;

        var lastNumActiveShapes = this.space.activeShapes.count;

        var now = Date.now();
        this.update(1/60);
        this.simulationTime += Date.now() - now;

        // Only redraw if the simulation isn't asleep.
        if (lastNumActiveShapes > 0) {
            now = Date.now();
            this.draw();
            this.drawTime += Date.now() - now;

        }
    };

    this.drawBack = function() {

    };

    //添加栏子
    this.addFence = function() {
        var width=300,height=120,kai=100;
        var offset = (this.width - width)/2;

        var space = this.space;
        var line1 = space.addShape(new cp.SegmentShape(space.staticBody, v(offset, height), v((width-kai)/2+offset, height+50), 1));
        line1.setElasticity(1);
        line1.setFriction(1);
        //line1.setCollisionType(FENCE_MASK);
        line1.setLayers(NOT_GRABABLE_MASK);

        var line2 = space.addShape(new cp.SegmentShape(space.staticBody, v(offset, height), v(offset, 0), 1));
        line2.setElasticity(1);
        line2.setFriction(1);
        //line2.setCollisionType();
        line2.setLayers(NOT_GRABABLE_MASK);

        var line3 = space.addShape(new cp.SegmentShape(space.staticBody, v((width-kai)/2+kai+offset, height+50), v(width+offset, height), 1));
        line3.setElasticity(1);
        line3.setFriction(1);
        //line1.setCollisionType(FENCE_MASK);
        line3.setLayers(NOT_GRABABLE_MASK);

        var line4 = space.addShape(new cp.SegmentShape(space.staticBody, v(width+offset, height), v(width+offset, 0), 1));
        line4.setElasticity(1);
        line4.setFriction(1);
        //line1.setCollisionType(FENCE_MASK);
        line4.setLayers(NOT_GRABABLE_MASK);

        var line5 = space.addShape(new cp.SegmentShape(space.staticBody, v((width-kai)/2+offset, height+50), v((width-kai)/2+kai+offset, height+50), 0));
        line5.setElasticity(1);
        line5.setFriction(1);
        line5.setCollisionType(FENCE_MASK);
        line5.setLayers(NOT_GRABABLE_MASK);
    };

    //添加墙
    this.addWalls = function() {
        var space = this.space;
        var wall1 = space.addShape(new cp.SegmentShape(space.staticBody, v(0, 0), v(0, this.height), 0));
        wall1.setElasticity(1);
        wall1.setFriction(1);
        wall1.setLayers(NOT_GRABABLE_MASK);

        var wall2 = space.addShape(new cp.SegmentShape(space.staticBody, v(this.width, 0), v(this.width, this.height), 0));
        wall2.setElasticity(1);
        wall2.setFriction(1);
        wall2.setLayers(NOT_GRABABLE_MASK);

        var floor1 = space.addShape(new cp.SegmentShape(space.staticBody, v(0, 0), v(this.width, 0), 0));
        floor1.setElasticity(1);
        floor1.setFriction(1);
        floor1.setLayers(NOT_GRABABLE_MASK);

        var floor2 = space.addShape(new cp.SegmentShape(space.staticBody, v(0, this.height), v(this.width, this.height), 0));
        floor2.setElasticity(1);
        floor2.setFriction(1);
        floor2.setLayers(NOT_GRABABLE_MASK);
    };
};

var raf = window.requestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.oRequestAnimationFrame
    || window.msRequestAnimationFrame
    || function(callback) {
        return window.setTimeout(callback, 1000 / 60);
    };

// Drawing helper methods

var drawCircle = function(ctx, scale, point2canvas, c, radius) {
    var c = point2canvas(c);
    ctx.beginPath();
    ctx.arc(c.x, c.y, scale * radius, 0, 2*Math.PI, false);
    ctx.fill();
    ctx.stroke();
};

var drawLine = function(ctx, point2canvas, a, b) {
    a = point2canvas(a); b = point2canvas(b);

    ctx.beginPath();
    ctx.moveTo(a.x, a.y);
    ctx.lineTo(b.x, b.y);
    ctx.stroke();
};

var drawRect = function(ctx, point2canvas, pos, size) {
    var pos_ = point2canvas(pos);
    var size_ = cp.v.sub(point2canvas(cp.v.add(pos, size)), pos_);
    ctx.fillRect(pos_.x, pos_.y, size_.x, size_.y);
};

var drawImage = function(ctx,imgsrc,point2canvas,r,pos,size) {
    var img = new Image();
    img.src = imgsrc;
    var pos_ = point2canvas(pos);
    var img_pos = v(0,0);
    var img_size = v(img.width,img.height);
    //var ra = size.x/2;
    //ctx.translate(pos_.x+ra,pos_.y+ra);
    //ctx.rotate(r);
    ctx.drawImage(img,img_pos.x,img_pos.y,img_size.x,img_size.y,pos_.x,pos_.y,size.x,size.y);
    //ctx.translate(0,0);
};

var springPoints = [
    v(0.00, 0.0),
    v(0.20, 0.0),
    v(0.25, 3.0),
    v(0.30,-6.0),
    v(0.35, 6.0),
    v(0.40,-6.0),
    v(0.45, 6.0),
    v(0.50,-6.0),
    v(0.55, 6.0),
    v(0.60,-6.0),
    v(0.65, 6.0),
    v(0.70,-3.0),
    v(0.75, 6.0),
    v(0.80, 0.0),
    v(1.00, 0.0)
];

var drawSpring = function(ctx, scale, point2canvas, a, b) {
    a = point2canvas(a); b = point2canvas(b);

    ctx.beginPath();
    ctx.moveTo(a.x, a.y);

    var delta = v.sub(b, a);
    var len = v.len(delta);
    var rot = v.mult(delta, 1/len);

    for(var i = 1; i < springPoints.length; i++) {

        var p = v.add(a, v.rotate(v(springPoints[i].x * len, springPoints[i].y * scale), rot));

        //var p = v.add(a, v.rotate(springPoints[i], delta));

        ctx.lineTo(p.x, p.y);
    }

    ctx.stroke();
};


// **** Draw methods for Shapes

cp.PolyShape.prototype.draw = function(ctx, scale, point2canvas)
{
    ctx.beginPath();

    var verts = this.tVerts;
    var len = verts.length;
    var lastPoint = point2canvas(new cp.Vect(verts[len - 2], verts[len - 1]));
    ctx.moveTo(lastPoint.x, lastPoint.y);

    for(var i = 0; i < len; i+=2){
        var p = point2canvas(new cp.Vect(verts[i], verts[i+1]));
        ctx.lineTo(p.x, p.y);
    }
    ctx.fill();
    ctx.stroke();
};

cp.SegmentShape.prototype.draw = function(ctx, scale, point2canvas) {
    if (this.r) {
        var oldLineWidth = ctx.lineWidth;
        ctx.lineWidth = Math.max(1, this.r * scale * 2);
        drawLine(ctx, point2canvas, this.ta, this.tb);
        ctx.lineWidth = oldLineWidth;
    }
};

cp.CircleShape.prototype.draw = function(ctx, scale, point2canvas) {
    //drawCircle(ctx, scale, point2canvas, this.tc, this.r);

    // And draw a little radian so you can see the circle roll.
    //drawLine(ctx, point2canvas, this.tc, cp.v.mult(this.body.rot, this.r).add(this.tc));
    var rot = cp.v.mult(this.body.rot, this.r).add(this.tc);
    var deg = get_angle(this.tc.x,this.tc.y,rot.x,rot.y);
    drawImage(ctx,
        this.ballinfo['img'],
        point2canvas,
        deg*Math.PI/180,
        v(this.tc.x-this.r,this.tc.y+this.r),
        v(this.r*2,this.r*2)
    );
};



// Draw methods for constraints

cp.PinJoint.prototype.draw = function(ctx, scale, point2canvas) {
    var a = this.a.local2World(this.anchr1);
    var b = this.b.local2World(this.anchr2);

    ctx.lineWidth = 2;
    ctx.strokeStyle = "grey";
    drawLine(ctx, point2canvas, a, b);
};

cp.SlideJoint.prototype.draw = function(ctx, scale, point2canvas) {
    var a = this.a.local2World(this.anchr1);
    var b = this.b.local2World(this.anchr2);
    var midpoint = v.add(a, v.clamp(v.sub(b, a), this.min));

    ctx.lineWidth = 2;
    ctx.strokeStyle = "grey";
    drawLine(ctx, point2canvas, a, b);
    ctx.strokeStyle = "red";
    drawLine(ctx, point2canvas, a, midpoint);
};

cp.PivotJoint.prototype.draw = function(ctx, scale, point2canvas) {
    //var a = this.a.local2World(this.anchr1);
    //var b = this.b.local2World(this.anchr2);
    //ctx.strokeStyle = "grey";
    //ctx.fillStyle = "grey";
    //drawCircle(ctx, scale, point2canvas, a, 2);
    //drawCircle(ctx, scale, point2canvas, b, 2);
};

cp.GrooveJoint.prototype.draw = function(ctx, scale, point2canvas) {
    var a = this.a.local2World(this.grv_a);
    var b = this.a.local2World(this.grv_b);
    var c = this.b.local2World(this.anchr2);

    ctx.strokeStyle = "grey";
    drawLine(ctx, point2canvas, a, b);
    drawCircle(ctx, scale, point2canvas, c, 3);
};

cp.DampedSpring.prototype.draw = function(ctx, scale, point2canvas) {
    var a = this.a.local2World(this.anchr1);
    var b = this.b.local2World(this.anchr2);

    ctx.strokeStyle = "grey";
    drawSpring(ctx, scale, point2canvas, a, b);
};

var randColor = function() {
    return Math.floor(Math.random() * 256);
};

var styles = [];
for (var i = 0; i < 100; i++) {
    styles.push("rgb(" + randColor() + ", " + randColor() + ", " + randColor() + ")");
}

cp.Shape.prototype.style = function() {
    var body;
    if (this.sensor) {
        return "rgba(255,255,255,0)";
    } else {
        body = this.body;
        if (body.isSleeping()) {
            return "rgb(50,50,50)";
        } else if (body.nodeIdleTime > this.space.sleepTimeThreshold) {
            return "rgb(170,170,170)";
        } else {
            return styles[this.hashid % styles.length];
        }
    }
};